package com.bw.flow

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.zip
import kotlinx.coroutines.launch

/**
 * 1.热流和冷流的区别？
 * 2.channel和flow的区别？
 * 3.flow的背压问题处理?
 *  背压问题：生产速度大于消费速度，导致管道中数据冗余
 *  解决：
 *  (1)collectLatest：只收集最新的数据，旧数据丢弃，正在执行旧数据也取消
 *  (2)conflate:收集最新数据，丢去中间的数据，正在执行旧数据不取消
 *  (3)buffer:缓冲操作符，将数据缓存起来，不会被丢弃，解决背压问题
 * 4.操作符：
 *
 * a.基本操作符：操作1个flow
 *  （1）collect：收集
 *  （2）collectLatest：收集最新数据，丢弃旧数据，解决背压问题
 * 	  conflate：合并操作符，收集最新数据，跳过中间数据，解决背压问题
 *  （3）map：就是用于将一个值映射成另一个值
 *  （4）filter：过滤   catch：捕获异常
 *  （5）onEach：遍历，辅助查看数据，和collect不一样，不会从管道取出数据
 *  （6）debounce：延迟处理数据，适用于搜索框，输入框等场景，我们不应该在用户每输入一个字符时就立刻发起一条网络请求，而是应该在一定时间的停顿之后再发起网络请求。如果用户停顿了一段时间，很有可能说明用户已经结束了快速输入，开始期待一些搜索建议了。这样就能有效地避免发起无效网络请求的情况
 *  （7）sample：按照指定时间间隔采集数据
 *
 * b。重点操作符：操作2个以上flow
 * （1）2个以上接口依赖串行，平级的写法搞定，不会出现嵌套地狱,可以使用以下三个，建议使用flatMapLatest
 *  	flatMapConcat:有顺序
 *  	flatMapMerge:无顺序
 *  	flatMapLatest:丢弃旧数据，处理新数据，建议使用，搜索功能实现
 *  （2）2个接口并行，保证同时请求完成做处理，使用zip
 *      一般情况：首页布局只有一个recyclerview，多布局展示数据，数据多个网络请求的数据(将多个接口数据展示在同一个recyclerview)
 *      设计好处；首页UI根据服务器下发数据展示还是不展示，顺序问题
 *
 *
 */
class MainActivity : AppCompatActivity() {
    private val viewModel: TestViewModel by lazy {
        ViewModelProvider(this)[TestViewModel::class.java]
    }
    private lateinit var editText: EditText
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        editText = findViewById(R.id.et)
        lifecycleScope.launch {
            editText.textChanges()
                .debounce(500) //延迟500毫秒
                .filter { it.toString().isNotEmpty() }
                .collect { query ->
                    println("发起网络请求: $query")
                }

        }

//        lifecycleScope.launch {
//            viewModel.flow1
//                //：SUSPEND 缓冲1个。当满了，挂起
//                .buffer(capacity = 2,onBufferOverflow = BufferOverflow.DROP_LATEST) //不影响发射，发射数据放在缓冲区中
//                .collect{
//                    println("flow1: $it")
//                    delay(3000)
//            }
//        }
//
//        val flow = flowOf(1,2,3,4,5)
//        lifecycleScope.launch {
//            flow
//                .onEach {
//                    println("flow1: $it")
//                }
//                .map { it * 2 }
//                .onEach {
//                    println("flow1: $it")
//                }
//                .filter {
//                    it < 6
//                }
//                .onEach {
//                    println("flow1: $it")
//                }
//                .catch {
//                    println("flow1: $it")
//                }
//                .collect{
//                    println("flow1: $it")
//                }
//        }
        lifecycleScope.launch {
            //嵌套地狱
//            getUserToken().collect{
//                println("用户token: $it")
//                getUserInfo(it).collect{
//                    println("用户信息: $it")
//                    getUserAddress(it).collect{
//                        println("用户地址: $it")
//                    }
//                }
//            }
            //串行接口，避免回调地狱
//            getUserToken()
//                .flatMapLatest {
//                    getUserInfo(it)
//                }.flatMapLatest {
//                    getUserAddress(it)
//                }.collect{
//                    println("用户地址: $it")
//                }
            //并行接口：获取天气信息
            getRealWeather()
                .zip(getWeather1()){realWeather,weather1->
                    println("天气信息: $realWeather $weather1")
                    //最后一行是输出
                    val list = mutableListOf(realWeather,weather1)
                    list
                }.zip(getWeather2()){list,weather2->
                    list.add(weather2)
                    list
                }.collect{list->
                   println("最终的结果$list")
                }

        }
    }
}
// 扩展函数：监听 EditText 文本变化
fun EditText.textChanges(): Flow<CharSequence?> = callbackFlow {
    val textWatcher = object : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
        override fun afterTextChanged(s: Editable?) {
            println("发射数据: ${s.toString()}")
            trySend(s)
        }
    }

    addTextChangedListener(textWatcher)
    awaitClose { removeTextChangedListener(textWatcher) }
}
//1,2,3 串行关系
//1：获得用户token
fun getUserToken() = flow {
    emit("token值")
}
//2：根据用户token获得用户名
fun getUserInfo(token:String) = flow {
    emit("用户名")
}
//3：根据用户名获得用户地址
fun getUserAddress(username:String) = flow {
    emit("用户地址")
}

// 4，5，6 并行关系
fun getRealWeather() = flow {
    emit("实时天气")
}
fun getWeather1() = flow {
    emit("未来7天")
}
fun getWeather2() = flow {
    emit("未来一个月")
}